package cron

import (
	"context"
	"fmt"
	log "gitee.com/xuyiping_admin/pkg/logger/logrus"

	"gitee.com/xuyiping_admin/pkg/xerr"
	prom "github.com/prometheus/client_golang/prometheus"
	"github.com/robfig/cron"
)

type Crontab struct {
	jobs   []job
	metric *prom.CounterVec
}

type job struct {
	action  func() error
	name    string
	formula string
	engine  *cron.Cron
}

func (j *job) AddFunc(spec string, cmd func()) error {
	return j.engine.AddFunc(spec, cmd)
}

func (j *job) Start() {
	j.engine.Start()
}

func (j *job) Stop() {
	j.engine.Stop()
}

func NewCrontab(metric *prom.CounterVec) *Crontab {
	return &Crontab{
		metric: metric,
		jobs:   []job{},
	}
}

func (ct *Crontab) Bind(name, formula string, action func() error) error {
	j := job{
		action:  action,
		name:    name,
		formula: formula,
		engine:  cron.New(),
	}
	ctx := context.Background()
	err := j.AddFunc(j.formula, func() {
		ierr := j.action()
		if ierr != nil {
			ct.metric.WithLabelValues(fmt.Sprintf("%s_fail", j.name)).Inc()
			log.WithError(ierr).Errorf("crontab %s failed", j.name)
			_ = xerr.ReportError(ctx, xerr.StackWithLog(ierr), j.name)
			return
		}
		ct.metric.WithLabelValues(fmt.Sprintf("%s_success", j.name)).Inc()
		log.Infof("crontab %s success", j.name)
	})
	if err != nil {
		return xerr.WithStack(err)
	}
	ct.jobs = append(ct.jobs, j)
	return nil
}

func (ct *Crontab) Run(stop <-chan struct{}) {
	defer func() {
		for _, job := range ct.jobs {
			job.engine.Stop()
		}
	}()
	log.Info("crontab start")

	for _, job := range ct.jobs {
		job.engine.Start()
	}
	<-stop
	log.Info("crontab stop")
}
